#include "exportobject.h"
#include "exporterutilitydesc.h"
#include "resource.h"
#include "maxtypes.h"
#include "stdmat.h"
#include <shaders.h>
#include "modstack.h"
#include <iskin.h>
#include "plugapi.h"
#include "matrix3.h"
#include "max.h"

template<typename T> T RAD(T x)
{
	(x*PI/180.0f)
}

//#define WLIGHT_OBJECT_CLASS_ID Class_ID(0x417114c2, 0x41f23d6a)
//#define WPARTICLE_OBJECT_CLASS_ID Class_ID(0x67172e0c, 0x2d5b5358)

UINT ModifierCount( INode *N )
{
	Object *obj = N->GetObjectRef ();

	UINT result = 0;

	while (obj->SuperClassID() == GEN_DERIVOB_CLASS_ID)
	{
		IDerivedObject *o = (IDerivedObject *) obj;
		result += o->NumModifiers ();
		obj = o->GetObjRef ();
	}

	return result;
}
Modifier *ModifierAtIndex (INode *node, unsigned i)
{
	Object *obj = node->GetObjectRef ();

	while (obj->SuperClassID() == GEN_DERIVOB_CLASS_ID)
	{
		IDerivedObject *o = (IDerivedObject *) obj;

		unsigned count = o->NumModifiers ();
		if (i < count)
		{
			return o->GetModifier (i);
		}
		else
		{
			i -= count;
			obj = o->GetObjRef ();
		}
	}

	return 0;
}

bool IsSkinModifier (INode *node, unsigned i)
{
	Modifier *m = ModifierAtIndex (node, i);

	return m->ClassID()==SKIN_CLASSID;
}

ISkin *ValidSkinModifier (INode *node)
{
	if ((ModifierCount (node) > 0) && IsSkinModifier (node, 0))
	{
		return (ISkin *) (ModifierAtIndex (node, 0)->GetInterface (I_SKIN));
	}
	else
	{
		return 0;
	}
}

bool IsSkinMesh (INode *node)
{
	return ValidSkinModifier (node)!=NULL;
}

bool HasObject (INode *node)
{
	return (node->EvalWorldState (0).obj != 0);
}

bool IsBone (INode *node)
{
	/*
	if (HasObject (node))
	{
	Class_ID id = node->EvalWorldState (0).obj->ClassID ();
	if (id == BX_BONE_OBJECT_CLASS_ID) return true;
	}

	Control *c = node->GetTMController ();

	if (c == 0) return false;

	return (c->ClassID () == BIPSLAVE_CONTROL_CLASS_ID);
	*/
	return false;
}

bool IsMesh (INode *node)
{
	//	if (IsBone (node)) return false;

	return node->EvalWorldState(0).obj->CanConvertToType(Class_ID (TRIOBJ_CLASS_ID, 0));
}

bool IsXRefObj (INode *node)
{
	//	if (IsBone (node)) return false;

	Object *obj = node->GetObjectRef();

	Class_ID CID = obj->ClassID();

	return obj->SuperClassID()==SYSTEM_CLASS_ID && CID == XREFOBJ_CLASS_ID;
}

/*bool IsOmniLight (INode *node)
{
return node->EvalWorldState(0).obj->ClassID()==Class_ID (OMNI_LIGHT_CLASS_ID, 0);
}


bool IsSpotLight (INode *node)
{
return
(node->EvalWorldState (0).obj->ClassID () == Class_ID (FSPOT_LIGHT_CLASS_ID, 0)) ||
(node->EvalWorldState (0).obj->ClassID () == Class_ID (SPOT_LIGHT_CLASS_ID, 0));
}

bool IsDirectionalLight (INode *node)
{
return
(node->EvalWorldState (0).obj->ClassID () == Class_ID (DIR_LIGHT_CLASS_ID, 0)) ||
(node->EvalWorldState (0).obj->ClassID () == Class_ID (TDIR_LIGHT_CLASS_ID, 0));
}*/

INode* FindRoot( INode *N )
{
	if ( !N->GetParentNode() || N->GetParentNode()==GetCOREInterface()->GetRootNode() ) return N;
	else return FindRoot( N->GetParentNode() );
}

////////////////////////////////////////////////////////////////////////////////////////////////
// sExportMaterial
////////////////////////////////////////////////////////////////////////////////////////////////
class FindTextureFilename : public AssetEnumCallback
{
public:
	FindTextureFilename() {}

	virtual void RecordAsset(const MaxSDK::AssetManagement::AssetUser& asset)
	{

		if ( std::string(asset.GetFileName()).substr(0, ExporterUtilityDesc::ClassInstance()->m_szMaxTexPath.size()) == ExporterUtilityDesc::ClassInstance()->m_szMaxTexPath )
		{
			//Fatal("Texture %s not in MaxTexPath.",Name);
		} else
		{
			m_szTextureName = std::string(asset.GetFileName()).substr(ExporterUtilityDesc::ClassInstance()->m_szMaxTexPath.size());
		}
	}

	std::string m_szTextureName;
};

void ExportVertex::Write(ostream &stream)
{
	stream << "\t<Vertex ID=\"" << id << "\" X=\""  << Vertex.x << "\" Y=\"" << Vertex.y << "\" Z=\"" << Vertex.z << "\" />" << endl;
}

void ExportFace::Write(ostream &stream, bool cvert)
{
	stream << "\t\t<Tri ID=\"" << id << "\" I1=\""  << vertex[0] << "\" I2=\"" << vertex[1] << "\" I3=\"" << vertex[2] << "\"";
	if (cvert)
	{
		stream << " CI1=\"" << color[0] << "\" CI2=\"" << color[1] << "\" CI3=\"" << color[2] << "\"";
	}
	stream << " NX=\"" << Normal.x << "\" NY=\"" << Normal.y << "\" NZ=\"" << Normal.z << "\"";
	stream << " SG=\"" << SmGroup <<"\"";
	stream << " />" << endl;
}

void ExportFace::CleanUpData()
{
	if (fabs(Normal.x) < NONZERO_THRESHOLD)
	{
		Normal.x = 0;
	}
	if (fabs(Normal.y) < NONZERO_THRESHOLD)
	{
		Normal.y = 0;
	}
	if (fabs(Normal.z) < NONZERO_THRESHOLD)
	{
		Normal.z = 0;
	}

}

void ExportMaterial::FillData( Mtl* Material )
{
	if ( !Material )
	{
		Name="---";

		SpecularLevel = 0;
		bTwoSided = false;
		bSpecular = false;

		Ambient.r = 1;
		Ambient.g = 1;
		Ambient.b = 1;

		Diffuse.r = 1;
		Diffuse.g = 1;
		Diffuse.b = 1;

		return;
	}

	Name = string((const char*)Material->GetName());

	{
		//Type = MAT_STANDARD;

		// Diffuse texture map
		Texmap* tMap = Material->GetSubTexmap(ID_DI);
		if ( tMap )
		{
			int submats = tMap->NumSubTexmaps();
			if ( submats )
			{
				//Type = MAT_MULTI;
				for ( int i = 0; i < submats; i++ )
				{
					Texmap *subtMap = tMap->GetSubTexmap( i );
					if (subtMap != 0)
					{
						FindTextureFilename findTextureFilename;
						subtMap->EnumAuxFiles( findTextureFilename, FILE_ENUM_DONT_RECURSE );
						m_szTextureName[i] = findTextureFilename.m_szTextureName;
					}
				}
			} else {
				FindTextureFilename findTextureFilename;
				tMap->EnumAuxFiles( findTextureFilename, FILE_ENUM_DONT_RECURSE );
				m_szTextureName[0] = findTextureFilename.m_szTextureName;
			}
		}

		if ( Material->ClassID() != Class_ID(DMTL_CLASS_ID,0) )
		{
			//ASSERT(false);
		}

		// Bumpmap
		tMap = Material->GetSubTexmap(ID_BU);
		if ( tMap )
		{
			//Type = MAT_BUMP;
			FindTextureFilename findTextureFilename;
			tMap->EnumAuxFiles( findTextureFilename, FILE_ENUM_DONT_RECURSE );
			m_szBumpName=findTextureFilename.m_szTextureName;
		}

		// Is there any reflection??
		tMap = Material->GetSubTexmap(ID_RL);
		bEnvironment = tMap!=NULL;
	}

	Shader *shader = ((StdMat2*)Material)->GetShader();
	SpecularLevel = shader?shader->GetSpecularLevel(0):0.f;
	bTwoSided = ((StdMat*)Material)->GetTwoSided();
	bSpecular = SpecularLevel>0.f;

	Ambient.r = Material->GetAmbient().r;
	Ambient.g = Material->GetAmbient().g;
	Ambient.b = Material->GetAmbient().b;

	Diffuse.r = Material->GetDiffuse().r;
	Diffuse.g = Material->GetDiffuse().g;
	Diffuse.b = Material->GetDiffuse().b;
}

void ExportMaterial::Write(ostream& stream)
{
	stream << "<Material";
	stream << " Name=\"" << Name << "\"";
	stream << " AmbientColor=\"" << Ambient.toRGB() << "\"";
	stream << " DiffuseColor=\"" << Diffuse.toRGB() << "\"";
	stream << " SpecularColor=\"" << Diffuse.toRGB() << "\"";
	if (m_szTextureName[0].size())
	{
		stream << " DiffuseBitmap=\"" << m_szTextureName[0] << "\"";
	}
	stream << "/>" << endl;

	//stream << "</Material>" << endl;
}
////////////////////////////////////////////////////////////////////////////////////////////////
// ExportObject
////////////////////////////////////////////////////////////////////////////////////////////////
void ExportObject::FillData( INode *N )
{
	Name = N->GetName();

	Matrix3 Matrix;
	Matrix3 Mat = N->GetObjectTM( 0 );
	Mat.SetColumn( 0, Mat.GetColumn(0)*-1 );
	if ( !bNoParent )
	{
		INode *Parent = N->GetParentNode();
		char* sz = Parent->GetName();
		Matrix3 InvMat = Parent->GetObjectTM( 0 );
		InvMat.SetColumn( 0, InvMat.GetColumn(0)*-1 );
		InvMat.Invert();
		Matrix = Mat*InvMat;
	} else {
		Matrix = Mat;
	}

	//Matrix.SetColumn( 0, Matrix.GetColumn(0)*-1 ); // Mirror to x axis
	this->Transformation = Matrix;
}

ExportObject* ExportObject::BuildFromINode( INode *N, bool bNoParent )
{
	ExportObject* result = NULL;
	TSTR TypeName;
	if ( !result )
	{
		if ( IsSkinMesh(N) )
		{
			result = new CExportWeightMesh();
		} else if ( IsMesh(N) )
		{
			result = new CExportMesh();
		}
		else
		{
			//DPrint("Unrecognizable node: %s, creating empty object...\n", N->GetName() );
			result = new CEmptyObject();
		}
	}

	result->bNoParent = bNoParent;

	result->FillData( N );
	return result;
}

void ExportObject::CreateHierarchyList( vector<string> &list )
{
	list.push_back( Name );
	for (vector<ExportObject*>::const_iterator it = Children.begin(); it != Children.end(); ++it)
		(*it)->CreateHierarchyList( list );
}

////////////////////////////////////////////////////////////////////////////////////////////////
// CEmptyObject
////////////////////////////////////////////////////////////////////////////////////////////////
void CEmptyObject::FillData( INode *N )
{
	ExportObject::FillData( N );

	// Export children
	if ( !bNoChildren )
		for ( int i = 0; i < N->NumberOfChildren(); i++ )
		{
			ExportObject *O = ExportObject::BuildFromINode(N->GetChildNode(i));
			if ( O )
				Children.push_back( O );
		}
}


